﻿#pragma warning disable 0168
using System;
using System.Collections.Generic;
using System.Linq;
using Celeriq.Client.Interfaces;
using Celeriq.Server.Interfaces;
using Celeriq.Utilities;
using System.IO;
using Celeriq.Common;
using System.ServiceModel;
using System.Runtime.Serialization;
using System.ServiceModel.Description;
using System.Security.Permissions;
using System.Security;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Runtime.InteropServices;

namespace Celeriq.Server.Core
{
    [Serializable()]
    [KnownType(typeof(RepositorySchema))]
    [KnownType(typeof(FieldDefinition))]
    [KnownType(typeof(DimensionDefinition))]
    [KnownType(typeof(IFieldDefinition))]
    [KnownType(typeof(Celeriq.Client.Interfaces.IRemotingObject))]
    [KnownType(typeof(Celeriq.Client.Interfaces.BaseRemotingObject))]
    [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.Single)]
    public class SystemCore : MarshalByRefObject, ISystemCore, IDisposable
    {
        #region Win32 Callout

        [DllImport("psapi.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool GetPerformanceInfo([Out] out PerformanceInformation PerformanceInformation, [In] int Size);

        [StructLayout(LayoutKind.Sequential)]
        public struct PerformanceInformation
        {
            public int Size;
            public IntPtr CommitTotal;
            public IntPtr CommitLimit;
            public IntPtr CommitPeak;
            public IntPtr PhysicalTotal;
            public IntPtr PhysicalAvailable;
            public IntPtr SystemCache;
            public IntPtr KernelTotal;
            public IntPtr KernelPaged;
            public IntPtr KernelNonPaged;
            public IntPtr PageSize;
            public int HandlesCount;
            public int ProcessCount;
            public int ThreadCount;
        }

        #endregion

        #region Class Members

        private System.Timers.Timer _timerDelayLoad = null;
        private System.Timers.Timer _timer = null;
        private System.Timers.Timer _timerStats = null;
        private ServerResourceSettings _resourceSettings = null;
        private object _statsLocker = new object();
        private System.Diagnostics.PerformanceCounter _cpuCounter;
        private Dictionary<Guid, int> _loadDelta = new Dictionary<Guid, int>();
        private Dictionary<Guid, int> _unloadDelta = new Dictionary<Guid, int>();
        private int _createdDelta = 0;
        private int _deletedDelta = 0;
        private RepositoryManager _manager = null;

        #endregion

        #region Constructors

        public SystemCore()
            : base()
        {
            _manager = new RepositoryManager(this);

            _timerDelayLoad = new System.Timers.Timer(2000);
            _timerDelayLoad.Elapsed += DelayStartup;
            _timerDelayLoad.Start();

            StatLogger.Initialize();
            //var q = StatLogger.Query(DateTime.Now.AddDays(-1), DateTime.Now);
            //var q = StatLogger.QueryRepositoryStats(Guid.Empty, DateTime.Now.AddDays(-1), DateTime.Now);
        }

        public RepositoryManager Manager
        {
            get { return _manager; }
        }

        private void DelayStartup(object sender, System.Timers.ElapsedEventArgs e)
        {
            try
            {
                _timerDelayLoad.Stop();
                _timerDelayLoad = null;

                Logger.LogInfo("Startup Started");
                lock (_manager)
                {
                    #region HouseKeeping

                    //Initialize user file if need be
                    InitUserFile();

                    #endregion

                    _resourceSettings = new ServerResourceSettings();
                    _resourceSettings.Initialize();

                    //Verify that all permissions are in place
                    if (!this.IsSetupValid())
                    {
                        throw new Exception("This application does not have the proper permissions!");
                    }

                    var timer = new Stopwatch();
                    timer.Start();

                    var files = Directory.GetFiles(ConfigurationSettings.DataPath, "*.celeriq");
                    Logger.LogInfo("Loading Data Path: '" + ConfigurationSettings.DataPath + "'. Repositories found: " + files.Length);

                    foreach (var f in files)
                    {
                        //var repository = new RepositoryDefinition();
                        //repository.Load(f);
                        //repository.CachePath = Path.Combine(ConfigurationSettings.DataPath, repository.ID.ToString());
                        //var remoteItem = new RemotingObjectCache() { Repository = repository, VersionHash = repository.VersionHash };
                        var fi = new FileInfo(f);
                        var repositoryId = fi.Name.Split('.').First();
                        _manager.AddRepository(new Guid(repositoryId));
                        //StartRepository(remoteItem.Repository.ID, this.RootUser);
                    }

                    timer.Stop();
                    Console.WriteLine("Core loaded " + timer.ElapsedMilliseconds + " ms");
                }
                Logger.LogInfo("Startup Finished");

                _cpuCounter = new System.Diagnostics.PerformanceCounter();
                _cpuCounter.CategoryName = "Processor";
                _cpuCounter.CounterName = "% Processor Time";
                _cpuCounter.InstanceName = "_Total";

                _timer = new System.Timers.Timer(20000);
                _timer.Elapsed += _timer_Elapsed;
                _timer.Start();

                _timerStats = new System.Timers.Timer(30000);
                _timerStats.Elapsed += _timerStats_Elapsed;
                _timerStats.Start();
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        #endregion

        #region Event Handlers

        private long GetProcessMemory()
        {
            try
            {
                var p = Process.GetProcessesByName(System.Reflection.Assembly.GetEntryAssembly().GetName().Name).FirstOrDefault();
                if (p != null) return p.PrivateMemorySize64;
                return 0;
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        private void _timerStats_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            try
            {
                #region CPU

                var cpu = (int)_cpuCounter.NextValue();

                #endregion

                #region Perf Counter

                var info = new PerformanceInformation();
                GetPerformanceInfo(out info, Marshal.SizeOf(info));

                #endregion

                #region In Mem

                var inMemCount = _manager.List.Count(x => x.GetIsLoaded());

                #endregion

                lock (_statsLocker)
                {
                    var newItem = new RealtimeStats
                               {
                                   Timestamp = DateTime.Now,
                                   MemoryUsageAvailable = info.PhysicalAvailable.ToInt64() * info.PageSize.ToInt64(),
                                   MemoryUsageProcess = GetProcessMemory(),
                                   MemoryUsageTotal = info.PhysicalTotal.ToInt64() * info.PageSize.ToInt64(),
                                   ProcessorUsage = cpu,
                                   RepositoryInMem = inMemCount,
                                   RepositoryLoadDelta = _loadDelta.Keys.Count,
                                   RepositoryUnloadDelta = _unloadDelta.Keys.Count,
                                   RepositoryTotal = _manager.List.Count(),
                                   RepositoryCreateDelta = _createdDelta,
                                   RepositoryDeleteDelta = _deletedDelta,
                               };
                    StatLogger.Log(newItem);

                    //Reset delta lists
                    _loadDelta = new Dictionary<Guid, int>();
                    _unloadDelta = new Dictionary<Guid, int>();
                    _createdDelta = 0;
                    _deletedDelta = 0;

                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
            }
        }

        private DateTime _lastGarbageColletion = DateTime.Now;
        private void _timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            try
            {
                lock (_manager)
                {
                    #region This will unload the data in a repository if there is a set # of minutes to keep inactive repository

                    var wasUnloaded = false;
                    foreach (var item in _manager.List.Where(x => x.GetIsLoaded()))
                    {
                        if (item.ServiceInstance.LastAccess != DateTime.MinValue &&
                            (_resourceSettings.AutoDataUnloadTime > 0) &&
                            (DateTime.Now.Subtract(item.ServiceInstance.LastAccess).TotalMinutes >= _resourceSettings.AutoDataUnloadTime))
                        {
                            item.ServiceInstance.UnloadData();
                            wasUnloaded = true;
                        }
                    }

                    #endregion

                    #region Unload if thre are too many repositories in memory

                    if (_resourceSettings.MaxRunningRepositories > 0)
                    {
                        var tooManyList = _manager.List
                            .Where(x => x.GetIsLoaded())
                            .OrderByDescending(x => x.ServiceInstance.LastAccess)
                            .Skip(_resourceSettings.MaxRunningRepositories)
                            .ToList();

                        foreach (var item in tooManyList)
                        {
                            item.ServiceInstance.UnloadData();
                            wasUnloaded = true;
                        }
                    }

                    #endregion

                    #region Unload if used too much memory

                    if (_resourceSettings.MaxMemory > 0)
                    {
                        long TOTAL = _resourceSettings.MaxMemory * 1024 * 1024;
                        var list = _manager.List.Where(x => x.GetIsLoaded()).OrderByDescending(x => x.ServiceInstance.LastAccess).ToList();
                        long usedMemory = 0;
                        foreach (var item in list)
                        {
                            usedMemory += item.ServiceInstance.GetDataMemorySize(RootUser);
                            if (usedMemory > TOTAL)
                            {
                                item.ServiceInstance.UnloadData();
                                wasUnloaded = true;
                            }
                        }
                    }

                    #endregion

                    //collect garbage to free memory
                    if (wasUnloaded || DateTime.Now.Subtract(_lastGarbageColletion).TotalMinutes > 10)
                    {
                        GC.Collect();
                        _lastGarbageColletion = DateTime.Now;
                    }

                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        #endregion

        #region Properties

        private object _cacheCryptedLocker = new object();
        private SystemCredentials _cacheCrypted = null;

        private SystemCredentials RootUser
        {
            get
            {
                try
                {
                    lock (_cacheCryptedLocker)
                    {
                        if (_cacheCrypted == null)
                        {
                            var user = UserDomain.UserList.FirstOrDefault(x => x.UserName == "root");
                            if (user == null) return null;
                            user = new SystemCredentials() { UserName = user.UserName, Password = user.Password };
                            user.Password = Celeriq.Utilities.SecurityHelper.Encrypt(this.GetPublicKey(), user.Password);
                            _cacheCrypted = user;
                            return user;
                        }
                        else
                        {
                            return _cacheCrypted;
                        }
                    }
                }
                catch (Exception ex)
                {
                    Logger.LogError(ex);
                    throw;
                }
            }
        }

        public int GetRepositoryCount(UserCredentials credentials)
        {
            lock (_manager)
            {
                return _manager.List.Count();
            }
        }

        public List<BaseRemotingObject> GetRepositoryPropertyList(UserCredentials credentials, int pageOffSet, int recordsPerPage)
        {
            if (!IsValidCredentials(credentials))
            {
                Logger.LogInfo("GetRepositoryPropertyList: Invalid credentials");
                throw new Exception("Invalid credentials");
            }

            if (pageOffSet < 1)
            {
                throw new Exception("PageOffset must be greater than 0.");
            }

            if (recordsPerPage < 1)
            {
                throw new Exception("RecordsPerPage must be greater than 0.");
            }

            Logger.LogInfo("GetRepositoryPropertyList Call Start");
            try
            {
                lock (_manager)
                {
                    var startIndex = (pageOffSet - 1) * recordsPerPage;
                    var tempList = _manager.List.OrderBy(x => x.Repository.Name).Skip(startIndex).Take(recordsPerPage).ToList();
                    foreach (var item in tempList)
                    {
                        item.ItemCount = item.ServiceInstance.GetItemCount(credentials);
                        item.DataDiskSize = item.ServiceInstance.GetDataDiskSize(credentials);
                        item.DataMemorySize = item.ServiceInstance.GetDataMemorySize(credentials);
                        item.IsLoaded = item.ServiceInstance.IsLoaded;
                    }

                    tempList.ForEach(x => x.IsLoaded = x.GetIsLoaded());
                    var retval = new List<BaseRemotingObject>();
                    tempList.ForEach(x => retval.Add((BaseRemotingObject)((ICloneable)x).Clone()));
                    return retval;
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                return null;
            }
            finally
            {
                Logger.LogInfo("GetRepositoryPropertyList Call End");
            }
        }

        #endregion

        #region Methods

        public void LogRepositoryPerf(RepositorySummmaryStats stat)
        {
            try
            {
                StatLogger.Log(stat);
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
            }
        }

        public string GetPublicKey()
        {
            return ConfigurationSettings.MasterKeys.PublicKey;
        }

        private static readonly Dictionary<string, string> _keyCache = new Dictionary<string, string>();

        public bool IsValidCredentials(UserCredentials credentials)
        {
            try
            {
                lock (_keyCache)
                {
                    var user = UserDomain.UserList.FirstOrDefault(x => x.UserName == credentials.UserName);
                    if (user == null) return false;

                    var prehash = string.Empty;

                    var cacheKey = ConfigurationSettings.MasterKeys.PrivateKey + "||" + credentials.Password;
                    if (_keyCache.ContainsKey(cacheKey))
                    {
                        prehash = _keyCache[cacheKey];
                    }
                    else
                    {
                        prehash = Celeriq.Utilities.SecurityHelper.Decrypt(ConfigurationSettings.MasterKeys.PrivateKey, credentials.Password);

                        //Cache
                        _keyCache.Add(cacheKey, prehash);
                    }
                    return (prehash == user.Password);
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                return false;
            }
        }

        private void InitUserFile()
        {
            try
            {
                if (UserDomain.UserList.Count == 0)
                {
                    UserDomain.AddUser(new SystemCredentials { UserName = "root", Password = "password" });
                }

            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        private bool IsSetupValid()
        {
            var writePermission = new FileIOPermission(FileIOPermissionAccess.Write, ConfigurationSettings.DataPath);
            if (!SecurityManager.IsGranted(writePermission))
            {
                Logger.LogError("The service does not have permission to access the install folder!");
                return false;
            }

            var eventLogPermission = new System.Diagnostics.EventLogPermission();
            try
            {
                eventLogPermission.Demand();
            }
            catch (Exception ex)
            {
                Logger.LogError("The service does not have permission to the event log!");
                return false;
            }
            return true;
        }

        public BaseRemotingObject SaveRepository(RepositorySchema repository, UserCredentials credentials)
        {
            if (!IsValidCredentials(credentials))
                throw new Exception("Invalid credentials");

            lock (_manager)
            {
                if (repository == null)
                    throw new Exception("Object cannot be null!");

                Logger.LogInfo("SaveRepository: " + repository.ID);
                if (repository.CreatedDate == DateTime.MinValue)
                    repository.CreatedDate = DateTime.Now;

                try
                {
                    var fileName = Path.Combine(ConfigurationSettings.DataPath, repository.ID.ToString() + ".celeriq");
                    repository.ToDisk(fileName);

                    if (!_manager.Contains(repository.ID))
                    {
                        lock (_statsLocker)
                        {
                            _createdDelta++;
                        }
                    }
                    _manager.AddRepository(repository.ID);
                    var active = _manager.List.FirstOrDefault(x => x.Repository.ID == repository.ID);

                    //Save state
                    StopRepository(active.Repository.ID, this.RootUser);

                    active.Repository = new RepositorySchema();
                    active.Repository.Load(fileName);

                    StartRepository(active.Repository.ID, this.RootUser);

                    //For now lets just try to stop and start the repository
                    ResetRepository(repository.ID);
                    active.VersionHash = repository.VersionHash;

                    StopRepository(active.Repository.ID, this.RootUser);

                    return ((ICloneable)((BaseRemotingObject)active)).Clone() as BaseRemotingObject;
                }
                catch (Exception ex)
                {
                    Logger.LogError(ex);
                    return null;
                }
            }
        }

        private void UnloadOldest()
        {
            try
            {
                var item = _manager.List
                    .OrderBy(x => x.ServiceInstance.LastAccess)
                    .FirstOrDefault();

                if (item != null)
                {
                    item.ServiceInstance.UnloadData();
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        private int LoadedCount()
        {
            try
            {
                return _manager.List.Count(x => x.GetIsLoaded());

            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public bool RepositoryExists(Guid repositoryId, UserCredentials credentials)
        {
            if (!IsValidCredentials(credentials))
                throw new Exception("Invalid credentials");

            try
            {
                lock (_manager)
                {
                    return _manager.List.Any(x => x.Repository.ID == repositoryId);
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public BaseRemotingObject DataLoadRepository(Guid repositoryId, UserCredentials credentials)
        {
            if (!IsValidCredentials(credentials))
                throw new Exception("Invalid credentials");

            try
            {
                lock (_manager)
                {
                    var active = _manager.List.FirstOrDefault(x => x.Repository.ID == repositoryId);
                    if (active == null) return null; //cannot find repository, should never happen
                    if (active.ServiceInstance == null) return null; //cannot find repository, should never happen
                    if (active.GetIsLoaded()) return ((ICloneable)((BaseRemotingObject)active)).Clone() as BaseRemotingObject;
                    active.ServiceInstance.LoadData(credentials);
                    return ((ICloneable)((BaseRemotingObject)active)).Clone() as BaseRemotingObject;
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public BaseRemotingObject StartRepository(Guid repositoryId, UserCredentials credentials)
        {
            if (!IsValidCredentials(credentials))
                throw new Exception("Invalid credentials");

            lock (_manager)
            {
                RemotingObjectCache active = null;
                try
                {
                    //If there is a maximum # of repository defined then stop least used if necessary
                    if ((_resourceSettings.MaxRunningRepositories > 0) && LoadedCount() >= _resourceSettings.MaxRunningRepositories)
                        UnloadOldest();

                    _manager.AddRepository(repositoryId);
                    active = _manager.List.FirstOrDefault(x => x.Repository.ID == repositoryId);
                    if (active == null) return null; //cannot find repository, should never happen
                        return ((ICloneable)((BaseRemotingObject)active)).Clone() as BaseRemotingObject; //if already has a host object then it is active, do not restart
                }
                catch (Exception ex)
                {
                    Logger.LogError(ex);
                    throw;
                }
            }

        }

        public BaseRemotingObject StopRepository(Guid repositoryId, UserCredentials credentials)
        {
            if (!IsValidCredentials(credentials))
                throw new Exception("Invalid credentials");

            lock (_manager)
            {
                try
                {
                    var active = _manager.List.FirstOrDefault(x => x.Repository.ID == repositoryId);
                    if (active == null) return null; //cannot find repository, should never happen
                    return ((ICloneable)((BaseRemotingObject)active)).Clone() as BaseRemotingObject;
                }
                catch (Exception ex)
                {
                    Logger.LogError(ex);
                    throw;
                }
            }

        }

        public void DeleteRepository(RepositorySchema repository, UserCredentials credentials)
        {
            if (!IsValidCredentials(credentials))
                throw new Exception("Invalid credentials");

            try
            {
                lock (_manager)
                {
                    var active = _manager.List.FirstOrDefault(x => x.Repository.ID == repository.ID);
                    if (active != null)
                    {
                        StopRepository(repository.ID, credentials);

                        var dataPath = Path.Combine(ConfigurationSettings.DataPath, repository.ID.ToString());
                        if (Directory.Exists(dataPath))
                            Directory.Delete(dataPath, true);
                        var fileName = Path.Combine(ConfigurationSettings.DataPath, repository.ID.ToString() + ".celeriq");
                        if (File.Exists(fileName))
                            File.Delete(fileName);

                        _manager.RemoveRepository(active.Repository.ID);
                        lock (_statsLocker)
                        {
                            _deletedDelta++;
                        }

                    }
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public void Compress(Guid repositoryId, UserCredentials credentials)
        {
            if (!IsValidCredentials(credentials))
                throw new Exception("Invalid credentials");

            lock (_manager)
            {
                var active = _manager.List.FirstOrDefault(x => x.Repository.ID == repositoryId);
                if (active == null) return; //cannot find repository, should never happen

                Logger.LogInfo("Compressing Repository: " + repositoryId.ToString());
                try
                {
                    active.ServiceInstance.Compress(credentials);
                }
                catch (Exception ex)
                {
                    Logger.LogError(ex);
                    throw;
                }
                Logger.LogInfo("Compressed Repository: " + repositoryId.ToString());
            }
        }

        /// <summary>
        /// Deletes all data from a repository and starts fresh
        /// </summary>
        private bool ResetRepository(Guid repositoryId)
        {
            lock (_manager)
            {
                var active = _manager.List.FirstOrDefault(x => x.Repository.ID == repositoryId);
                if (active == null) return false; //cannot find repository, should never happen

                Logger.LogInfo("Resetting Repository: " + repositoryId.ToString());
                try
                {
                    var startUp = new Celeriq.Server.Interfaces.ServiceStartup()
                                  {
                                      RepositoryFolder = ConfigurationSettings.DataPath,
                                      RepositorySchema = active.Repository,
                                      CacheLength = 300,
                                  };

                    active.ServiceInstance.Reset(startUp);

                }
                catch (Exception ex)
                {
                    Logger.LogError(ex);
                    throw;
                }

                Logger.LogInfo("Reset Repository: " + repositoryId.ToString());
                return true;
            }
        }

        /// <summary>
        /// Shuts down all repositories
        /// </summary>
        public void ShutDown()
        {
            try
            {
                lock (_manager)
                {
                    StatLogger.Shutdown();

                    Logger.LogInfo("ShutDown Start of " + _manager.List.Count() + " items");
                    foreach (var item in _manager.List)
                    {
                        this.StopRepository(item.Repository.ID, this.RootUser);
                    }
                    ConfigurationSettings.Save();
                    Logger.LogInfo("ShutDown Complete");
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public bool ExportSchema(Guid repositoryId, UserCredentials credentials, string backupFile)
        {
            if (!IsValidCredentials(credentials))
                throw new Exception("Invalid credentials");

            try
            {
                lock (_manager)
                {
                    var active = _manager.List.FirstOrDefault(x => x.Repository.ID == repositoryId);
                    if (active == null) return false;

                    var startUp = new Celeriq.Server.Interfaces.ServiceStartup()
                                  {
                                      RepositoryFolder = ConfigurationSettings.DataPath,
                                      RepositorySchema = active.Repository,
                                      CacheLength = 300,
                                  };

                    active.ServiceInstance.ExportSchema(startUp, credentials, backupFile);
                    return true;
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public bool Backup(Guid repositoryId, UserCredentials credentials, string backupFile)
        {
            if (!IsValidCredentials(credentials))
                throw new Exception("Invalid credentials");

            try
            {
                lock (_manager)
                {
                    var active = _manager.List.FirstOrDefault(x => x.Repository.ID == repositoryId);
                    if (active == null)
                    {
                        return false;
                    }

                    var startUp = new Celeriq.Server.Interfaces.ServiceStartup()
                                  {
                                      RepositoryFolder = ConfigurationSettings.DataPath,
                                      RepositorySchema = active.Repository,
                                      CacheLength = 300,
                                  };

                    active.ServiceInstance.Backup(startUp, credentials, backupFile);
                    return true;
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public bool Restore(Guid repositoryId, UserCredentials credentials, string backupFile)
        {
            if (!IsValidCredentials(credentials))
                throw new Exception("Invalid credentials");

            try
            {
                lock (_manager)
                {
                    var active = _manager.List.FirstOrDefault(x => x.Repository.ID == repositoryId);
                    if (active == null) return false;

                    var startUp = new Celeriq.Server.Interfaces.ServiceStartup()
                                  {
                                      RepositoryFolder = ConfigurationSettings.DataPath,
                                      RepositorySchema = active.Repository,
                                      CacheLength = 300,
                                  };

                    active.ServiceInstance.Restore(startUp, credentials, backupFile);
                    return true;
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public string[] GetBackups(UserCredentials credentials)
        {
            if (!IsValidCredentials(credentials))
                throw new Exception("Invalid credentials");

            try
            {
                var retval = new List<string>();
                var backupFolder = Path.Combine(ConfigurationSettings.DataPath, "..", "Backup");
                if (Directory.Exists(backupFolder))
                {
                    var files = Directory.GetFiles(backupFolder);
                    foreach (var file in files)
                    {
                        var fi = new FileInfo(file);
                        retval.Add(fi.Name);
                    }
                }
                return retval.ToArray();
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public ServerResourceSettings GetServerResourceSetting(UserCredentials credentials)
        {
            if (!IsValidCredentials(credentials))
                throw new Exception("Invalid credentials");

            lock (_manager)
            {
                return _resourceSettings;
            }
        }

        public bool SaveServerResourceSetting(UserCredentials credentials, ServerResourceSettings settings)
        {
            if (!IsValidCredentials(credentials))
                throw new Exception("Invalid credentials");

            try
            {
                lock (_manager)
                {
                    _resourceSettings = settings;
                    _resourceSettings.Save();
                    return true;
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public ProfileItem[] GetProfile(Guid repositoryId, UserCredentials credentials, long lastProfileId)
        {
            if (!IsValidCredentials(credentials))
                throw new Exception("Invalid credentials");

            try
            {
                var active = _manager.List.FirstOrDefault(x => x.Repository.ID == repositoryId);
                if (active == null || active.ServiceInstance == null)
                {
                    return new ProfileItem[] { };
                }
                else if (!active.ServiceInstance.IsLoaded)
                {
                    return new ProfileItem[] { };
                }
                return active.ServiceInstance.GetProfile(credentials, lastProfileId);
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public bool AddSystemUser(UserCredentials credentials, UserCredentials user)
        {
            try
            {
                lock (_manager)
                {
                    return UserDomain.AddUser(new SystemCredentials() { UserName = user.UserName, Password = user.Password });
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public bool DeleteSystemUser(UserCredentials credentials, UserCredentials user)
        {
            try
            {
                lock (_manager)
                {
                    return UserDomain.DeleteUser(new SystemCredentials() { UserName = user.UserName, Password = user.Password });
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public UserCredentials[] GetUserList(UserCredentials credentials)
        {
            if (!IsValidCredentials(credentials))
                throw new Exception("Invalid credentials");
            
            try
            {
                lock (_manager)
                {
                    var retval = new List<UserCredentials>();
                    UserDomain.UserList.ToList().ForEach(x => retval.Add((UserCredentials)((ICloneable)x).Clone()));
                    retval.ForEach(x => x.Password = string.Empty);
                    return retval.ToArray();
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public void NotifyLoad(Guid repositoryId)
        {
            try
            {
                lock (_statsLocker)
                {
                    if (_loadDelta.ContainsKey(repositoryId))
                        _loadDelta[repositoryId]++;
                    else
                        _loadDelta.Add(repositoryId, 1);
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public void NotifyUnload(Guid repositoryId)
        {
            try
            {
                lock (_statsLocker)
                {
                    if (_unloadDelta.ContainsKey(repositoryId))
                        _unloadDelta[repositoryId]++;
                    else
                        _unloadDelta.Add(repositoryId, 1);

                    LogRepositoryPerf(new RepositoryStats
                    {
                        ActionType = RepositoryActionConstants.Unload,
                        Elapsed = 0,
                        RepositoryId = repositoryId,
                    });
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public RealtimeStats[] PerformanceCounters(UserCredentials credentials, DateTime minDate, DateTime maxDate)
        {
            if (!IsValidCredentials(credentials))
                throw new Exception("Invalid credentials");
            
            try
            {
                lock (_statsLocker)
                {
                    return StatLogger.QueryServerStats(minDate, maxDate).ToArray();
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public RepositorySummmaryStats GetRepositoryStats(UserCredentials credentials, Guid repositoryId, DateTime minDate, DateTime maxDate)
        {
            if (!IsValidCredentials(credentials))
                throw new Exception("Invalid credentials");
            
            try
            {
                lock (_statsLocker)
                {
                    return StatLogger.QueryRepositoryStats(repositoryId, minDate, maxDate);
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public SystemStats GetSystemStats()
        {
            try
            {
                var info = new PerformanceInformation();
                GetPerformanceInfo(out info, Marshal.SizeOf(info));
                var retval = new SystemStats()
                             {
                                 MachineName = Environment.MachineName,
                                 OSVersion = Environment.OSVersion.ToString(),
                                 ProcessorCount = Environment.ProcessorCount,
                                 TickCount = Environment.TickCount,
                                 TotalMemory = info.PhysicalTotal.ToInt64()*info.PageSize.ToInt64(),
                             };
                return retval;
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        #endregion

        void IDisposable.Dispose()
        {
            this.ShutDown();
        }

    }
}